﻿/*=============================================================================

  Library: HMPBundleFrame

  Copyright (c) German Cancer Research Center,
    Division of Medical and Biological Informatics

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

=============================================================================*/

#include "Bundle_p.h"
#include "IBundleContext.h"
#include "BundleContext_p.h"
#include "BundleFrameworkContext_p.h"
#include "BundleFrameworkLauncher.h"
#include "Services_p.h"
#include "IServiceRegistration.h"
#include "IServiceReference.h"
#include "ServiceReference_p.h"

#include <stdexcept>

//----------------------------------------------------------------------------
BundleContextPrivate::BundleContextPrivate(BundlePrivate* plugin)
  : plugin(plugin)
{}

//----------------------------------------------------------------------------
void BundleContextPrivate::isPluginContextValid() const
{
  if (!plugin) {
    throw IllegalStateException("This plugin context is no longer valid");
  }
}

//----------------------------------------------------------------------------
void BundleContextPrivate::invalidate()
{
  plugin = 0;
}

//----------------------------------------------------------------------------
IBundleContext::IBundleContext(BundlePrivate* plugin)
  : d_ptr(new BundleContextPrivate(plugin))
{}

//----------------------------------------------------------------------------
IBundleContext::~IBundleContext()
{
  Q_D(BundleContext);
  delete d;
}

//----------------------------------------------------------------------------
QVariant IBundleContext::getProperty(const QString& key) const
{
  Q_D(const BundleContext);
  d->isPluginContextValid();
  return d->plugin->fwCtx->props.value(key);
}

std::string IBundleContext::getProperty(const std::string &key) const
{
    // QVarint 类型不确定，这里这么操作qVarint有问题，以后改
    Q_D(const BundleContext);
    d->isPluginContextValid();
    QString qkey( key.data());
    QVariant var = d->plugin->fwCtx->props.value(qkey);

//    Properties a = d->plugin->fwCtx->props;
//    if(QString == var.type())
    QString str = var.toString();

    std::string pro = str.toStdString();

    return pro;
}

//----------------------------------------------------------------------------
QSharedPointer<IBundle> IBundleContext::getPlugin() const
{
  Q_D(const BundleContext);
  d->isPluginContextValid();
  return d->plugin->q_func();
}

IBundle* IBundleContext::getBundle() const
{
   QSharedPointer<IBundle> shareBnd = getPlugin();

   return shareBnd.data();

}

//----------------------------------------------------------------------------
QSharedPointer<IBundle> IBundleContext::getPlugin(long id) const
{
  Q_D(const BundleContext);
  return d->plugin->fwCtx->plugins->getPlugin(id);
}

//----------------------------------------------------------------------------
QList<QSharedPointer<IBundle> > IBundleContext::getPlugins() const
{
  Q_D(const BundleContext);
  d->isPluginContextValid();
  return d->plugin->fwCtx->plugins->getPlugins();
}

void IBundleContext::getBundles(std::vector<IBundle*> &bundles) const
{
    QList<QSharedPointer<IBundle> > listShareBund = getPlugins();
    QList<QSharedPointer<IBundle> >::Iterator it = listShareBund.begin();

    for(; it != listShareBund.end(); it++)
    {
        bundles.push_back( (*it).data() );
    }
}

//----------------------------------------------------------------------------
QSharedPointer<IBundle> IBundleContext::installPlugin(const QUrl& location, QIODevice* in)
{
  Q_D(BundleContext);
  d->isPluginContextValid();

  return d->plugin->fwCtx->plugins->install(location, in);

}

IBundle *IBundleContext::installBundle(const std::string &location, bool bCopyBundle)
{
    Q_UNUSED(bCopyBundle);

    QString qstr( location.data());
    QUrl url(qstr);
    QSharedPointer<IBundle> bndptr = installPlugin(url);

    return bndptr.data();
}

bool IBundleContext::uninstallBundle(IBundle::Ptr pBundle)
{
    try
    {
        pBundle->uninstall();
        return true;
    }
    catch(const Exception &e)
    {
      qDebug() << "Exception wrong:"<< e.what();
      return false;
    }

}

bool IBundleContext::resolveBundle(IBundle::Ptr pBundle)
{

    QSharedPointer<IBundle> pbnd(pBundle.data());
    quint32 sta = PluginFrameworkLauncher::stdresolvePlugin(pbnd);
    if((sta & IBundle::RESOLVED) || (sta & IBundle::ACTIVE) || (sta & IBundle::STARTING) )
        return true;
    else
        return false;
}

bool IBundleContext::startBundle(IBundle::Ptr pBundle)
{
    std::string stdstr = pBundle->getSymbolicName();
    QString symbolName(stdstr.data());
    return PluginFrameworkLauncher::start(symbolName);

}

bool IBundleContext::stopBundle(IBundle::Ptr pBundle)
{
    std::string stdstr = pBundle->getSymbolicName();
    QString symbolName(stdstr.data());
    return PluginFrameworkLauncher::stop(symbolName);
}

//----------------------------------------------------------------------------
QFileInfo IBundleContext::getDataFile(const QString& filename)
{
  Q_D(BundleContext);
  d->isPluginContextValid();
  QDir dataRoot(d->plugin->getDataRoot().absolutePath());
  if (!dataRoot.exists())
  {
    if (!dataRoot.mkpath(dataRoot.absolutePath()))
    {
      qWarning() << "Could not create persistent storage area:" << dataRoot.absolutePath();
    }
  }
  return QFileInfo(dataRoot.absolutePath() + '/' + filename);
}

std::string IBundleContext::pathForLibrary(const std::string &libraryName)
{
    QString qfilename(libraryName.data());
    QFileInfo filepath = getDataFile(qfilename);
    QString strfile = filepath.absolutePath();

    return strfile.toStdString();
}

//----------------------------------------------------------------------------
IServiceRegistration IBundleContext::registerService(const QStringList& clazzes, QObject* service, const Dictionary& properties)
{
  Q_D(BundleContext);
  d->isPluginContextValid();
  return d->plugin->fwCtx->services->registerService(d->plugin, clazzes, service, properties);
}

//----------------------------------------------------------------------------
IServiceRegistration IBundleContext::registerService(const char* clazz, QObject* service, const Dictionary& properties)
{
  Q_D(BundleContext);
  d->isPluginContextValid();
  QStringList clazzes;
  clazzes.append(clazz);
  return d->plugin->fwCtx->services->registerService(d->plugin, clazzes, service, properties);
}
//----------------------------------------------------------------------------
IServiceRegistration IBundleContext::registerService(const std::list<std::string>& clazzes, QObject* service, const Dictionary& properties)
{
    QStringList qclazz;
    std::list<std::string>::const_iterator it = clazzes.begin();
    for(; it != clazzes.end(); it++)
    {
        QString qstr( (*it).data() );
        qclazz.push_back(qstr);
    }

    return registerService(qclazz, service, properties);
}
//----------------------------------------------------------------------------
QList<IServiceReference> IBundleContext::getServiceReferences(const QString& clazz, const QString& filter)
{
  Q_D(BundleContext);
  d->isPluginContextValid();
  return d->plugin->fwCtx->services->get(clazz, filter, 0);
}
//----------------------------------------------------------------------------
void IBundleContext::getServiceReferences(const std::string& clazz, std::list<IServiceReference> results,const std::string& filter)
{
    QString qclazz(clazz.data());
    QString qfilter(filter.data());

    QList<IServiceReference> listRst =getServiceReferences(qclazz,qfilter);
    QList<IServiceReference>::iterator itr = listRst.begin();
    for(; itr != listRst.end(); itr++)
    {
        results.push_back( *itr );
    }
}
//----------------------------------------------------------------------------
IServiceReference IBundleContext::getServiceReference(const QString& clazz)
{
  Q_D(BundleContext);
  d->isPluginContextValid();
  return d->plugin->fwCtx->services->get(d->plugin, clazz);
}
//----------------------------------------------------------------------------

IServiceReference IBundleContext::getServiceReference(const std::string& clazz)
{
    QString qclazz(clazz.data());
    return getServiceReference(qclazz);

}

//----------------------------------------------------------------------------
QObject* IBundleContext::getService(const IServiceReference& reference)
{
  Q_D(BundleContext);
  d->isPluginContextValid();

  if (!reference)
  {
    throw InvalidArgumentException("Default constructed ServiceReference is not a valid input to getService()");
  }
  IServiceReference internalRef(reference);
  return internalRef.d_func()->getService(d->plugin->q_func());
}

//----------------------------------------------------------------------------
bool IBundleContext::ungetService(const IServiceReference& reference)
{
  Q_D(BundleContext);
  d->isPluginContextValid();
  IServiceReference ref = reference;
  return ref.d_func()->ungetService(d->plugin->q_func(), true);
}

//----------------------------------------------------------------------------
bool IBundleContext::connectPluginListener(const QObject* receiver, const char* slot,
                                             Qt::ConnectionType type)
{
  Q_D(BundleContext);
  d->isPluginContextValid();
  // TODO check permissions for a direct connection
  if (type == Qt::DirectConnection || type == Qt::BlockingQueuedConnection)
  {
    return receiver->connect(&(d->plugin->fwCtx->listeners), SIGNAL(pluginChangedDirect(BundleEvent)), slot, type);
  }
  else if (type == Qt::QueuedConnection)
  {
    return receiver->connect(&(d->plugin->fwCtx->listeners), SIGNAL(pluginChangedQueued(BundleEvent)), slot, type);
  }
  else
  {
    throw InvalidArgumentException("Only Qt::DirectConnection, Qt::QueuedConnection, or Qt::BlockingQueuedConnection are allowed as type argument.");
  }
}

//----------------------------------------------------------------------------
void IBundleContext::disconnectPluginListener(const QObject *receiver, const char* slot)
{
  Q_D(BundleContext);
  d->isPluginContextValid();

  QObject::disconnect(&(d->plugin->fwCtx->listeners), SIGNAL(pluginChangedDirect(BundleEvent)), receiver, slot);
  QObject::disconnect(&(d->plugin->fwCtx->listeners), SIGNAL(pluginChangedQueued(BundleEvent)), receiver, slot);
}

//----------------------------------------------------------------------------
bool IBundleContext::connectFrameworkListener(const QObject* receiver, const char* slot, Qt::ConnectionType type)
{
  Q_D(BundleContext);
  d->isPluginContextValid();
  // TODO check permissions for a direct connection
  return receiver->connect(&(d->plugin->fwCtx->listeners), SIGNAL(frameworkEvent(BundleFrameworkEvent)), slot, type);
}

//----------------------------------------------------------------------------
void IBundleContext::disconnectFrameworkListener(const QObject *receiver, const char* slot)
{
  Q_D(BundleContext);
  d->isPluginContextValid();

  QObject::disconnect(&(d->plugin->fwCtx->listeners), SIGNAL(frameworkEvent(BundleFrameworkEvent)), receiver, slot);
}

//----------------------------------------------------------------------------
void IBundleContext::connectServiceListener(QObject* receiver, const char* slot,
                                             const QString& filter)
{
  Q_D(BundleContext);
  d->isPluginContextValid();
  d->plugin->fwCtx->listeners.addServiceSlot(getPlugin(), receiver, slot, filter);
}

//----------------------------------------------------------------------------
void IBundleContext::disconnectServiceListener(QObject* receiver,
                                                const char* slot)
{
  Q_D(BundleContext);
  d->isPluginContextValid();
  d->plugin->fwCtx->listeners.removeServiceSlot(getPlugin(), receiver, slot);
}
